/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine

	MaxsiString.cpp
	Functions for easier string handling

****************************************************************************/

#include "MaxsiEngine.h"
#include <iostream> 
#include <math.h>

using namespace std;

namespace MaxsiEngine
{
	MESTR* EvaluateQuote(MESTR* Buffer)
	{
		//size_t	OutLen	=	MESTRLEN(Buffer)-2;
		//MESTR*	Out		=	new MESTR[OutLen+1];
		//memcpy(Out,Buffer+1,OutLen);
		//Out[OutLen]		=	0;
		//return Out;
		MESTR*	Out			=	BuildString(1,Buffer);
		size_t	OutLen		=	MESTRLEN(Out);
		size_t	Written		=	0;

		for (size_t	N = 0; N < OutLen; N++)
		{
			if ( Buffer[N] == '"' )
			{
			}
			else if ( Buffer[N] == '\\' && N+1 < OutLen && Buffer[N+1] == '"' )
			{
				Out[Written]	=	Buffer[N+1];
				Written			+=	1;
				N++;
			}
			else
			{
				Out[Written]	=	Buffer[N];
				Written			+=	1;
			}
		}

		Out[Written]			=	0;

		return	Out;
	}

	size_t __forceinline SkipWhiteSpace(MESTR* Buffer)
	{
		return SkipWhiteSpace(Buffer,MESTRLEN(Buffer));
	}
	size_t __forceinline SkipWhiteSpace(MESTR* Buffer, size_t BufferLen)
	{
		for (size_t I = 0; I < BufferLen; I++)
		{
			if (	Buffer[I] !=	' '
				&&	Buffer[I] !=	'	'
				&&	Buffer[I] !=	'\n'
				&&	Buffer[I] !=	'\r')
			{
				return I;
			}
		}
		return SIZE_MAX;
	}
	size_t __forceinline NextWhiteSpace(MESTR* Buffer)
	{
		return NextWhiteSpace(Buffer,MESTRLEN(Buffer));
	}
	size_t __forceinline NextWhiteSpace(MESTR* Buffer, size_t BufferLen)
	{
		bool	bQuote	=	false;

		for (size_t I = 0; I < BufferLen; I++)
		{
			if (	Buffer[I] ==	'"'){bQuote=!bQuote;}
			if (	!bQuote &&			(
					Buffer[I] ==	' '
				||	Buffer[I] ==	'	'
				||	Buffer[I] ==	'\n'
				||	Buffer[I] ==	'\r'))
			{
				return I;
			}
		}
		return SIZE_MAX;
	}
	size_t __forceinline NextLine(MESTR* Buffer)
	{
		return NextLine(Buffer,MESTRLEN(Buffer));
	}
	size_t __forceinline NextLine(MESTR* Buffer, size_t BufferLen)
	{
		for (size_t I = 0; I < BufferLen; I++)
		{
			if (	Buffer[I] ==	'\n'
				||	Buffer[I] ==	'\r')
			{
				return I;
			}
		}
		return SIZE_MAX;
	}
	size_t FindChar(MESTR* Buffer, char CharToFind, size_t BufferLen)
	{
		for (size_t I = 0; I < BufferLen; I++)
		{
			if ( Buffer[I] == CharToFind)
			{
				return I;
			}
		}
		return -1;
	}
	size_t FindCharPrev(MESTR* Buffer, char CharToFind, size_t Begin)
	{
		for (size_t I = Begin+1; I>0; I--)
		{
			if ( Buffer[I-1] == CharToFind)
			{
				return I-1;
			}
		}
		return SIZE_MAX;
	}

	size_t FindString(MESTR* String1, MESTR* String2)
	{
		return FindStringEx(String1, MESTRLEN(String1), String2, MESTRLEN(String2));
	}

	size_t FindStringEx(MESTR* String1, size_t String1Len, MESTR* String2, size_t String2Len)
	{
		size_t X = 0, Y = 0;

		if ( String2Len > String1Len ) { return SIZE_MAX; } // String 2 cannot possibly fit into string 1!

		String1Len	-=	String2Len;

		// Loop through X and check at each X if Y follows, if so return X
		for (X = 0; X < String1Len; X++)
		{
			for (Y = 0; Y < String2Len; Y++)
			{
				if ( String1[X+Y] != String2[Y] )
				{
					break; // Nope.
				}
			}
			if ( Y == String2Len )
			{
				return X; // Found at location X
			}
		}
		return SIZE_MAX; // Not Found!

	}

	MESTR* BuildString(unsigned int NumParameters, ...)
	{
		va_list param_pt;

		va_start(param_pt,NumParameters); // Call the setup macro
		
		// First calculate the string lenght

		size_t FinalString_Len = 0;
		MESTR* TMPString = 0;

		for (unsigned int I = 0 ; I < NumParameters ; I++)
		{
			TMPString = va_arg(param_pt,MESTR*);
			if ( TMPString )
			{
				FinalString_Len+=MESTRLEN(TMPString);
			}
		}
		FinalString_Len++;


		// Allocate the required string
		MESTR* FinalString = new MESTR[FinalString_Len+10];

		if ( FinalString == NULL )
		{
			ME_UNDEFINED_BEHAVIORMESTRC("String Allocation Failed in BuildString()!");
			return NULL;
		}

		FinalString[0] = 0;

		va_end(param_pt); // Closing macro
		va_start(param_pt,NumParameters); // Call the setup macro

		for (unsigned int I = 0 ; I < NumParameters ; I++)
		{
			TMPString = va_arg(param_pt,MESTR*);
			if ( TMPString )
			{
				MESTRCAT(FinalString,FinalString_Len,TMPString);
			}
		}
		
		return FinalString;
	}

	bool str_replace(MESTR* input, MESTR* find, MESTR* replace)
	{
		if (!input||!find||!replace) { return false; }
		size_t input_Len = MESTRLEN(input);
		size_t find_Len = MESTRLEN(find);
		size_t replace_Len = MESTRLEN(replace);

		return buf_replace(input,input_Len,find,find_Len,replace,replace_Len);
	}

	bool buf_replace(MESTR* input,size_t input_Len, MESTR* find, size_t find_Len, MESTR* replace, size_t replace_Len)
	{
		if (!input||!find||!replace) { return false; }

		if (find_Len != replace_Len)
		{
			return false;
		}

		size_t X = 0, Y = 0;

		// Loop through X and check at each X if Y follows, if so return X
		for (X = 0; X < input_Len; X++)
		{
			for (Y = 0; Y < find_Len; Y++)
			{
				if ( input[X+Y] != find[Y] )
				{
					break; // Nope.
				}
			}
			if ( Y == find_Len )
			{
				memcpy(input+X,replace,find_Len);
			}
		}
		return true; // Done
	}

	// The algorithm used here is not the fastest but I'll have to do. It's fairly simple and
	// as such should be easy to maintain. It attempts to reallocate as little memory as possible.
	MESTR*	StringReplace(MESTR* Search, MESTR* Insert, MESTR* Haystack)
	{
		// Cache the string lengths!
		size_t	SearchLen	=	MESTRLEN(Search);
		size_t	InsertLen	=	MESTRLEN(Insert);
		size_t	HaystackLen	=	MESTRLEN(Haystack);
		size_t	Searchable	=	HaystackLen-SearchLen+1;
		size_t	X			=	0;
		size_t	Y			=	0;

		// Is what we are looking for longer than what we are looking in?
		if ( HaystackLen < SearchLen )
		{
			// Yep! Just return our Haystack back again, no need to do any working.
			return Haystack;
		}

		// Decide whether we would need memory allocations!
		if ( SearchLen == InsertLen )
		{
			// Yay! What we need to replace is the same length as what we search for!

			// Find any occurrence of what we are looking for
			for ( X = 0; X < Searchable; X++ )
			{
				for ( Y = 0; Y < SearchLen; Y++ )
				{
					if ( Haystack[X+Y] != Search[Y] ) { break; }
				}
				if ( Y == SearchLen )
				{
					// We found one instance of what we are looking for! Go ahead and
					// replace the text in the haystack!
					for ( Y = 0; Y < SearchLen; Y++ )
					{
						Haystack[X+Y] = Insert[Y];
					}
					X += SearchLen-1; // Skip a bit forward in X, we don't wanna be recursive
				}
			}
			
			return Haystack;
		}
		else if ( InsertLen < SearchLen )
		{
			// Yay! What we need to insert is shorter than what we need to replace!

			size_t	Diff	=	SearchLen-InsertLen;

			// Find any occurrence of what we are looking for
			for ( X = 0; X < Searchable; X++ )
			{
				for ( Y = 0; Y < SearchLen; Y++ )
				{
					if ( Haystack[X+Y] != Search[Y] ) { break; }
				}
				if ( Y == SearchLen )
				{
					// We found one instance of what we are looking for! Go ahead and
					// replace the text in the haystack!
					for ( Y = 0; Y < InsertLen; Y++ )
					{
						Haystack[X+Y] = Insert[Y];
					}
					HaystackLen -= Diff; // Make the original string a bit shorter
					for ( size_t N = X+InsertLen; N < HaystackLen; N++ )
					{
						Haystack[N] = Haystack[N+Diff];
					}
					Haystack[HaystackLen] = 0; // Null-terminate the shorter string					
					X += SearchLen-1-Diff; // Skip a bit forward in X, we don't wanna be recursive
				}
			}

			return Haystack;
		}
		else
		{
			// Oh no! We need to allocate more buffer space! First count how much space
			// we need in our new string.

			size_t	RequiredBufferSpace	=	HaystackLen;
			size_t	Occurrences			=	0;

			// Count how many times what we search for occurs in Haystack!
			for ( X = 0; X < Searchable; X++ )
			{
				for ( Y = 0; Y < SearchLen; Y++ )
				{
					if ( Haystack[X+Y] != Search[Y] ) { break; }
				}
				if ( Y == SearchLen )
				{
					Occurrences++;
					X += SearchLen-1; // Skip a bit forward in X, we don't wanna be recursive
				}
			}

			if ( Occurrences == 0 )
			{
				// Haha, would you know that, the string wasn't even found!
				return Haystack;
			}
			else
			{
				// FIXME: THIS CASE IS BROKEN ATM.

				// Figure out how much buffer space is needed!
				if ( InsertLen < SearchLen )
				{
					RequiredBufferSpace	-= (SearchLen-InsertLen)*Occurrences;
				}
				else
				{
					RequiredBufferSpace	+= (InsertLen-SearchLen)*Occurrences;
				}

				// Allocate an appropriate buffer!
				MESTR*		WhatDoYouCallAHaystackWithoutANeedle			=	new MESTR[RequiredBufferSpace+1];
				
				if ( WhatDoYouCallAHaystackWithoutANeedle == NULL ) { return NULL; }

				WhatDoYouCallAHaystackWithoutANeedle[RequiredBufferSpace]	=	0;

				size_t		HP			=	0;

				// Find any occurrence of what we are looking for
				for ( X = 0; X < RequiredBufferSpace; X++ )
				{
					for ( Y = 0; Y < SearchLen; Y++ )
					{
						if ( Haystack[HP+Y] != Search[Y] ) { break; }
					}
					if ( Y == SearchLen )
					{
						// We found one instance of what we are looking for! Go ahead and
						// replace the text in the needle-less haystack!
						for ( Y = 0; Y < InsertLen; Y++ )
						{
							WhatDoYouCallAHaystackWithoutANeedle[X+Y] = Insert[Y];
						}
						X	+=	InsertLen; // Skip a bit forward in X, we don't wanna be recursive
						HP	+=	SearchLen; // Skip a bit forward in HP, we don't wanna be recursive
					}
					
					WhatDoYouCallAHaystackWithoutANeedle[X] = Haystack[HP];
					HP++;
				}

				delete[] Haystack;
				return WhatDoYouCallAHaystackWithoutANeedle;
			}
		}
	}	

	MESTR* SubString(size_t Start, size_t Length, MESTR* ExtractFrom)
	{
		if ( ExtractFrom ==	NULL ) { return NULL; }

		size_t	AvailableLength		=	MESTRLEN(ExtractFrom);

		if ( Start > AvailableLength ) { return NULL; } // Yeah, that's not going to happen.
		if ( Start + Length > AvailableLength ) { Length = AvailableLength-Start; }

		MESTR*	Result		=	new MESTR[Length+1];

		if ( Result )
		{
			memcpy(Result, ExtractFrom+Start, Length);
			Result[Length]	=	0;
		}

		return Result;
	}

	LINK size_t	HEXTOI			(MESTR* Text)
	{
		if (!Text) { return 0; }
		size_t	TextLen		=		MESTRLEN(Text);
		size_t	Exponent	=		0;
		size_t	Out			=		0;
		char	Value		=		0;
		if (!TextLen) { return 0; }
		for (size_t N = TextLen-1; true; N--)
		{
			Value = -1;
			if (Text[N] >= '0' && Text[N] <= '9')
			{
				Value = Text[N] - '0';
			}
			if (Text[N] >= 'a' && Text[N] <= 'f')
			{
				Value = Text[N] - 'a' + 10;
			}
			if (Text[N] >= 'A' && Text[N] <= 'F')
			{
				Value = Text[N] - 'A' + 10;
			}
			if (Value!=-1)
			{
				Out += Value * (int)pow((double)16.0f,(double)Exponent);
				Exponent++;
			}
			if ( N == 0) { break; }
		}
		return Out;
	}

	// 32 bit signed integer, base 10
	LINK	MESTR*	IToA(int	In)
	{
		MESTR*	Result	=	new MESTR[12]; // ceiling(32*ln(2)/ln(10)) + room for a leading '-' + null
		MESTRITOA(In,Result,12,10);
		return	Result;
	}

	// TODO: There is no function called _uitoa_s!!! Simply pass it on to the 64-bit version
	// 32 bit unsigned integer, base 10
	LINK	MESTR*	UIToA(unsigned int	In)
	{
		return UIToA64((unsigned __int64)In);
		//MESTR*	Result	=	new MESTR[11]; // ceiling(32*ln(2)/ln(10)) + null
		//_uitoa_s(In,Result,11,10);
		//return	Result;
	}
	
	// 64 bit signed integer, base 10
	LINK	MESTR*	IToA64(__int64	In)
	{
		MESTR*	Result	=	new MESTR[22]; // ceiling(64*ln(2)/ln(10)) + room for a leading '-' + null
		MESTRITOA64(In,Result,22,10);
		return	Result;
	}

	// 64 bit unsigned integer, base 10
	LINK	MESTR*	UIToA64(unsigned __int64	In)
	{
		MESTR*	Result	=	new MESTR[21]; // ceiling(64*ln(2)/ln(10)) + null
		MESTRUITOA64(In,Result,21,10);
		return	Result;
	}

	// double floating point value, base 10
	LINK	MESTR*	DToA(double	In, int Digits, int StringLen)
	{
		char*	Result	=	new char[StringLen*sizeof(MESTR)]; // Oh god, this is a lot of bytes. 
		_gcvt_s(Result,StringLen,In,Digits);
		if ( strlen(Result) && Result[strlen(Result)-1] == '.' ) { Result[strlen(Result)-1] = 0; }
		
		#ifdef ME_USE_MULTIBYTE_STRING
		// If we use multibyte strings, convert it to multibyte strings from ANSI

		for ( size_t I = strlen(Result)+1; I!=-1; I-- )
		{
			((MESTR*)Result)[I] = (char)Result[I];
		}

		#endif
		return	(MESTR*)Result;
	}


	// TODO: Improve, expand, implement, use.
	LINK	inline	int		decomchars	(MESTR** data, MESTR* A, MESTR* B, char Na, char Nb)
	{
		int		datacache	=	**data;
		return datacache;
	}

	LINK	char	FromHEXA(char Hex)
	{
		if ( Hex >= '0' && Hex <= '9' )
		{
			return Hex-'0';
		}
		else if ( Hex >= 'a' && Hex <= 'f' )
		{
			return Hex-'a'+10;
		}
		else if ( Hex >= 'A' && Hex <= 'F' )
		{
			return Hex-'A'+10;
		}
		return 0;
	}

	LINK	wchar_t	FromHEXW(wchar_t Hex)
	{
		if ( Hex >= '0' && Hex <= '9' )
		{
			return Hex-'0';
		}
		else if ( Hex >= 'a' && Hex <= 'f' )
		{
			return Hex-'a'+10;
		}
		else if ( Hex >= 'A' && Hex <= 'F' )
		{
			return Hex-'A'+10;
		}
		return 0;
	}

	LINK	MESTR	FromHEXM(MESTR Hex)
	{
		if ( Hex >= '0' && Hex <= '9' )
		{
			return Hex-'0';
		}
		else if ( Hex >= 'a' && Hex <= 'f' )
		{
			return Hex-'a'+10;
		}
		else if ( Hex >= 'A' && Hex <= 'F' )
		{
			return Hex-'A'+10;
		}
		return 0;
	}
	
	
	LINK	short	ToHEXA(char In)
	{
		short	Result;

		if ( (In%16) < 10 )
		{
			Result	=	'0' + In%16;
		}
		else
		{
			Result	=	'A' + In%16 - 10;
		}
		
		if ( (In/16) < 10 )
		{
			Result	+=	('0' + In/16)*256;
		}
		else
		{
			Result	+=	('A' + In/16)*256;
		}

		return Result;
	}
	
	LINK	int	ToHEXW(wchar_t In)
	{
		return ToHEXA(In/256)*256+ToHEXA(In%256);
	}

	LINK	MESTRL	ToHEXM(MESTR In)
	{
		#ifdef ME_USE_MULTIBYTE_STRING
			return ToHEXW(In);
		#else
			return ToHEXA(In);
		#endif
	}

	LINK	bool	ShouldCharBeEncodedURL(MESTR In)
	{
		if ( 'a' <= In && 'z' >= In )
		{
			return false;
		}
		else if ( 'A' <= In && 'Z' >= In )
		{
			return false;
		}
		else if ( '0' <= In && '9' >= In )
		{
			return false;
		}
		else if ( In == '/' || In == '\\' )
		{
			return false;
		}
		else if ( In == ':' )
		{
			return true;
		}
		else if ( In == '_' || In == '-')
		{
			return false;
		}
		else if ( In == 0 )
		{
			return false;
		}
		return true;
	}

	LINK	bool	ShouldCharBeEncodedFileName(MESTR In)
	{
		if ( 'a' <= In && 'z' >= In )
		{
			return false;
		}
		else if ( 'A' <= In && 'Z' >= In )
		{
			return false;
		}
		else if ( '0' <= In && '9' >= In )
		{
			return false;
		}
		else if ( In == '/' || In == '\\' )
		{
			return true;
		}
		else if ( In == ':' )
		{
			return true;
		}
		else if ( In == '_' || In == '-')
		{
			return false;
		}
		else if ( In == ' ')
		{
			return false;
		}
		else if ( In == 0 )
		{
			return false;
		}
		return true;
	}

	LINK	bool	DecodeURL(char* URL)
	{
		size_t	URLLen	=	strlen(URL);
		size_t	Ahead	=	0;

		for ( size_t I = 0; I < URLLen; I++ )
		{
			if ( URL[I] == '%' )
			{
				if ( I-URLLen < 2 ) { return false; }
				URL[I-Ahead]	=	FromHEXA(URL[I+1])*16+FromHEXA(URL[I+2]);
				Ahead			+=	2;
				I				+=	2;
			}
			else
			{
				URL[I-Ahead]	=	URL[I];
			}			
		}

		URL[URLLen-Ahead]		=	0;

		return true;
	}

	LINK	char*	EncodeURL(char* URL)
	{
		if ( URL == NULL ) { char* Result = new char[1]; Result[0] = 0; return Result; }

		size_t	URLLen	=	strlen(URL);
		size_t	EncLen	=	URLLen;

		for ( size_t I = 0; I < URLLen; I++ )
		{
			if ( ShouldCharBeEncodedURL(URL[I]) )
			{
				EncLen+=2;
			}
		}

		char*	Result	=	new char[EncLen+1];
		EncLen			=	0;

		for ( size_t I = 0; I <= URLLen; I++ )
		{
			if ( ShouldCharBeEncodedURL(URL[I]) )
			{
				short	Hexed		=	ToHEXA(URL[I]);
				Result[I+EncLen]	=	'%';
				Result[I+EncLen+1]	=	(char)(Hexed/256);
				Result[I+EncLen+2]	=	(char)(Hexed%256);
				EncLen+=2;
			}
			else
			{
				Result[I+EncLen]	=	URL[I];
			}
		}

		return Result;
	}

	LINK	MESTR*	EncodeFileName(MESTR* URL)
	{
		if ( URL == NULL ) { MESTR* Result = new MESTR[1]; Result[0] = 0; return Result; }

		size_t	URLLen	=	MESTRLEN(URL);
		size_t	EncLen	=	URLLen;

		for ( size_t I = 0; I < URLLen; I++ )
		{
			if ( ShouldCharBeEncodedFileName(URL[I]) )
			{
				EncLen+=2;
			}
		}

		MESTR*	Result	=	new MESTR[EncLen+1];
		EncLen			=	0;

		for ( size_t I = 0; I <= URLLen; I++ )
		{
			if ( ShouldCharBeEncodedFileName(URL[I]) )
			{
				short	Hexed		=	ToHEXM(URL[I]);
				Result[I+EncLen]	=	'%';
				Result[I+EncLen+1]	=	(char)(Hexed/256);
				Result[I+EncLen+2]	=	(char)(Hexed%256);
				EncLen+=2;
			}
			else
			{
				Result[I+EncLen]	=	URL[I];
			}
		}

		return Result;
	}

	LINK	size_t	strlen_s		(MESTR* String, size_t MaxLen)
	{
		for ( size_t Result = 0; Result < MaxLen; Result++ )
		{
			if ( String[Result] == 0 ) { return Result; }
		}
		return SIZE_MAX; // Not found!
	}

	
	LINK	char*		WCHAR2CHAR(wchar_t* Input)
	{
		size_t	ResultLen	=	wcslen(Input);
		char*	Result		=	new char[ResultLen+1];
		
		for ( size_t I = 0; I <= ResultLen; I++ )
		{
			Result[I] = Input[I]%256;
		}

		return Result;
	}

	LINK	wchar_t*	CHAR2WCHAR(char* Input)
	{
		size_t	ResultLen	=	strlen(Input);
		wchar_t*	Result	=	new wchar_t[ResultLen+1];
		
		for ( size_t I = 0; I <= ResultLen; I++ )
		{
			Result[I] = Input[I];
		}

		return Result;
	}
}